iT邦幫忙

2025 iThome 鐵人賽

DAY 1
0
Rust

用刷題來練RUST系列 第 1

用刷題來練RUST Day 1 前言&字串String

  • 分享至 

  • xImage
  •  

為何想學Rust

常看到許多應用程式用Rust改寫後效率提升不少的消息,加上在刷題的過程中想挑一個編譯的程式來練習於是就選了Rust。

為何是以刷題的形式

看了介紹文件API文件,卡在知道但做不到所以從頭開始依照Rust關鍵字和Leetcode tag來邊解題邊學Rust,簡單介紹題目來源由後直接開始用刷題來練RUST。

&str V.S. String

在宣告字串時常用let s1 = "Hello";方式,Rust背後的機制是在編譯時先宣告好"Hello"記憶體大小,s1只是個 reference,存放Hello"指標和長度,由於已經宣告好了所以無法對原本字串做修改。

fn main() {
    let s1: &str = "Hello";

    println!("ptr = {:p}", s1.as_ptr());  // ptr = 0x62fe8a79a0ab
    println!("len = {}", s1.len());       // len = 5
}

image.png

來源:https://rust-lang.tw/book-tw/ch04-01-what-is-ownership.html

為何需要String

在編譯時知道有固定記憶大小的會寫在堆疊(Stack),還不知道大小的會做堆積上配置(allocating on the heap)先把一塊夠大的空位,標記為已佔用,然後回傳一個指標(pointer),當進行一連串的操作讓原本空間不夠時,Rust會再去找一塊更大的空間來儲放,更詳細的內容可以參考Vec<T>就是一個堆積上配置例子,String是以Vec<u8>封裝的型別,因此我們可以對String型別的字串做修改

fn main() {
    let mut s = String::from("hello world");
    println!("{:?}", s); //"hello world"
    s.push_str(" !!!");
    println!("{:?}", s); //"hello world !!!"
}

切片(slice)

在&str中我們可以透過[start..end]來拿取想要的字串,String如果也想做切片(slice)可以先&String解參考成&str

fn main() {
    let s = String::from("hello world");

    let hello = &s[0..5];
    let world = &s[6..11];
}


圖片來源:https://rust-lang.tw/book-tw/ch04-03-slices.html

有了String和slice的概念就能實作leetcode 1768.

Leetcode 1768. Merge Strings Alternately

題目:將兩個String型別的單字依序組合成新單字,如果一個單字長度用完了,剩下的全接在後面。

輸入:word1 = "abc", word2 = "pqr"

輸出:"apbqcr"

限制:

  • 1 <= word1.length, word2.length <= 100

  • word1 and word2 只包含英文小寫字母

這題因為測資全都是英文小寫字母,每個字元正好佔 1 byte,可以用切片方式拿到每一個記憶體位置的字元。

use std::cmp::min;

impl Solution {
    pub fn merge_alternately(word1: String, word2: String) -> String {
        let mut new = String::new();
        for i in 0..min(word1.len(),word2.len()) {
            new.insert_str(i*2, &word1[i..i+1]);
            new.insert_str(i*2+1, &word2[i..i+1]);
        }
        if word1.len()>word2.len(){
            new.push_str(&word1[word2.len()..]);
        } else {
            new.push_str(&word2[word1.len()..]);
        }
        new
    }
}

如果單字是UTF-8譬如繁體中文,因為一個字元佔3byte就無法用上面解法,需要用chars()將String轉成迭代器再操作

fn main() {
    let s = String::from("繁");
    println!("{:?}", s.as_bytes());//印出[231, 185, 129]
    println!("{}", &s[0..1]);
    //切的位置不符合panic
    //thread 'main' panicked at src/main.rs:4:22:
	//byte index 1 is not a char boundary; it is inside '繁' (bytes 0..3) of `繁`
}

fn main() {
    let s = String::from("繁");
    println!("{:?}", s.as_bytes());//印出[231, 185, 129]
    println!("{:?}", s.chars());//印出Chars(['繁'])
}

impl Solution {
    pub fn merge_alternately(word1: String, word2: String) -> String {
        let mut new = String::new();
        let mut iter1 = word1.chars();
        let mut iter2 = word2.chars();

        loop {
            match (iter1.next(), iter2.next()) {
                (Some(c1), Some(c2)) => {
                    new.push(c1);
                    new.push(c2);
                }
                (Some(c1), None) => {
                    new.push(c1);
                    new.extend(iter1);
                    break;
                }
                (None, Some(c2)) => {
                    new.push(c2);
                    new.extend(iter2);
                    break;
                }
                (None, None) => break,
            }
        }
        new
    }
}

Day1 總結

  1. &str 編譯時就固定了不能修改,String可以修改
  2. 字串編碼不一定等於1 byte,用char()方法可以幫我們拿到字元

參考資料

  1. https://doc.rust-lang.org/book/
  2. https://doc.rust-lang.org/stable/std/index.html
  3. https://kaochenlong.com/2023/09/23/stack-and-heap.html

系列文
用刷題來練RUST1
圖片
  熱門推薦
圖片
{{ item.channelVendor }} | {{ item.webinarstarted }} |
{{ formatDate(item.duration) }}
直播中

尚未有邦友留言

立即登入留言